<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      Function.prototype.call = function (context, ...args) {
        context ??= globalThis;
        context.fn = this;
        const result = context.fn(...args);
        delete context.fn;
        return result;
      };

      Function.prototype.apply = function (context, argsArr) {
        context ??= globalThis;
        context.fn = this;
        const args = Array.isArray(argsArr) ? argsArr : [];
        const result = context.fn(...args);
        delete context.fn;
        return result;
      };

      Function.prototype.bind = function (context, ...args) {
        context ??= globalThis;
        const that = this;
        function boundFunction(...argsArr) {
          context = this instanceof boundFunction ? boundFunction : context; // new 调用 this指向 实例对象
          context.fn = that;
          const result = context.fn(...args, ...argsArr);
          delete context.fn;
          return result;
        }
        return boundFunction;
      };

      Array.prototype.forEach = function (fn) {
        if (!fn) {
          throw new Error();
        }
        let _this = arguments[1] ? arguments[1] : globalThis;
        for (let i = 0; i < this.length; i++) {
          fn.apply(_this, [this[i], i, this]);
        }
      };

      Array.prototype.filter = function (fn) {
        const arr = [];
        let _this = arguments[1] ? arguments[1] : globalThis;
        for (let i = 0; i < this.length; i++) {
          if (fn.apply(_this, [this[i], i, this])) {
            arr.push(this[i]);
          }
        }
        return arr;
      };
      Array.prototype.map = function (fn) {
        const arr = [];
        let _this = arguments[1] ? arguments[1] : globalThis;
        for (let i = 0; i < this.length; i++) {
          arr.push(fn.apply(_this, [this[i], i, this]));
        }
        return arr;
      };

      Array.prototype.every = function (fn) {
        let _this = arguments[1] ? arguments[1] : globalThis;
        const flag = true;
        for (let i = 0; i < this.length; i++) {
          if (!fn.apply(_this, [this[i], i, this])) {
            flag = false;
            break;
          }
        }
        return flag;
      };

      Array.prototype.some = function (fn) {
        let _this = arguments[1] ? arguments[1] : globalThis;
        const flag = false;
        for (let i = 0; i < this.length; i++) {
          if (!fn.apply(_this, [this[i], i, this])) {
            flag = true;
            break;
          }
        }
        return flag;
      };

      Array.prototype.reduce = function (fn, initValue) {
        let _this = arguments[2] ? arguments[2] : globalThis;
        var nextValue = initValue ?? this[0];
        for (let i = initValue ? 0 : 1; i < this.length; i++) {
          nextValue = fn.apply(_this, [nextValue, this[i], i, this]);
        }
        return nextValue;
      };

      // 函数 科里化
      function curry() {
        let fn = arguments[0];
        let arr = Array.prototype.slice.call(arguments, 1); // 获取 开始时传入的剩余参数
        if (arr.length === fn.length) {
          //fn.length  本身函数 该传入的参数数量
          return fn.apply(this, arr);
        }
        function _curry() {
          let arg = Array.prototype.slice.call(arguments, 0);
          arr = arr.concat(arg);
          // arr.push(...arguments);
          if (arr.length === fn.length) {
            return fn.apply(this, arr);
          }
          return _curry;
        }
        return _curry;
      }
      function add(a, b, c) {
        return a + b + c;
      }

      console.log(curry(add)(1)(2)(3)); // 6
      console.log(curry(add, 1)(2)(3)); // 6
      console.log(curry(add, 1, 2, 3)); // 6
      console.log(curry(add, 1)(3, 4)); // 8
    </script>
  </body>
</html>
